package com.zrc.japktool.apk.merge;

import com.zrc.japktool.apk.merge.manifest.ManifestXmlEditor;
import com.zrc.japktool.apk.zipalignsign.Apk4kAligner;
import com.zrc.japktool.apk.zipalignsign.ApkSign;
import com.zrc.japktool.util.AxmlUtil;
import com.zrc.japktool.util.FileUtil;
import com.zrc.japktool.util.TaskLog;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.CompressionLevel;
import net.lingala.zip4j.model.enums.CompressionMethod;
import pxb.android.axml.Axml;
import pxb.android.axml.AxmlReader;
import pxb.android.axml.AxmlWriter;
import java.io.*;
import java.nio.file.Paths;
import java.util.*;

/**
 * ARSC文件编辑器，支持对APK中的resources.arsc进行编辑操作
 */
public class ApkMerge {

    private TaskLog log=null;

    public ApkMerge(TaskLog log) {
        this.log=log;
    }

    /**
     * 合并两个APK的resources.arsc资源
     */
    public void apkMerge(String aApkPath, String bApkPath,String logoPicPath,AssetsConfig appConfig) throws IOException {
        String aApkName = new File(aApkPath).getName().replace(".apk", "");
        String bApkName = new File(bApkPath).getName().replace(".apk", "");
        String outApkPath = new File(aApkPath).getParent() + File.separator +aApkName+ "-merged" + ".apk";
        if (new File(outApkPath).exists()) {
            new File(outApkPath).delete();
        }
        try {
            ManifestXmlEditor aManifestXmlEditor = new ManifestXmlEditor(aApkPath);
            ManifestXmlEditor bManifestXmlEditor = new ManifestXmlEditor(bApkPath);

            // 加载A APK的arsc编辑器 加载B APK的arsc编辑器
            ArscEditor aEditor = new ArscEditor(aApkPath);
            ArscEditor bEditor = new ArscEditor(bApkPath);
            Map<String, String> idMap =  aEditor.merge(bEditor);

            //修改appName
            String appNameHexId = aManifestXmlEditor.getAppNameId();
            log.info("appNameHexId-->" + appNameHexId);
            aEditor.modifyAppName(appConfig.getAppName(),appNameHexId);

            //找到logoID
            List<String> ids =aManifestXmlEditor.getAppLogoIds();
            for (String id : ids) {
                log.info("logoHexId-->" + id);
            }

            // 重新压缩为新APK
            FileUtil.copyFile(aApkPath,outApkPath);
            //Files.copy(Paths.get(aApkPath),Paths.get(outApkPath));
            delFileWithApk(outApkPath);

            Set<String> logoPaths =aEditor.findLogoPath(ids);
            PicEditor picEditor = new PicEditor(logoPicPath);
            picEditor.save2ApkFile(outApkPath,logoPaths);

            //拷贝资源
            copyApkFiles(bApkPath,outApkPath,idMap);

            // 保存修改后的arsc文件到A的临时目录
            aEditor.pkg.name=appConfig.getAppPackage();//修改arsc的包名
            aEditor.save2ApkFile(outApkPath.replace("\\", "/"));

            //dex合并
            DexMerge dexMerge = new DexMerge();
            dexMerge.merge(aApkPath, bApkPath, outApkPath, idMap);

            //合并前替换b.apk中AndroidManifest.xml 所有属性中包名
            bManifestXmlEditor.replaceAllNodeAttr(appConfig.getAppPackage());
            //修改b.apk中的横竖屏模式
            bManifestXmlEditor.modifyScreenOrientation(appConfig.isAppScreenmode());
            //aManifestXmlEditor.modifyApplicationTheme(bManifestXmlEditor.getApplicationTheme());
            //AndroidManifest.xml资源合并
            aManifestXmlEditor.merge(bManifestXmlEditor,outApkPath,idMap,appConfig);
            log.tip("合并完成，输出APK: " + outApkPath);

            //  对齐
            String ziplan_inApkName = new File(outApkPath).getName().replace(".apk", "").replace("\\", "/");
            String ziplan_inApkPath = outApkPath;
            File ziplan_inApkFile = new File(ziplan_inApkPath);
            File ziplan_outApkFile = new File(ziplan_inApkFile.getParent(), ziplan_inApkName + "-zipalign.apk");
            String ziplan_outApkPath = ziplan_outApkFile.getAbsolutePath().replace("\\", "/");
            Apk4kAligner apk4kAligner = new Apk4kAligner(log);
            apk4kAligner.zipAlign(ziplan_inApkPath, ziplan_outApkPath, false);
            //ZipAlign.zipAlign(ziplan_inApkPath, ziplan_outApkPath,log);

            // 签名
            String sign_inApkPath = ziplan_outApkPath;
            File sign_inApkFile = new File(sign_inApkPath);
            String sign_outApkPath = new File(sign_inApkFile.getParent(), ziplan_inApkName + "-zipalign-sign.apk").getAbsolutePath().replace("\\", "/");

            ApkSign apkSign = new ApkSign(log);
            //apkSign.sign("android", "androiddebugkey", "android", false, Paths.get(sign_inApkPath), Paths.get(sign_outApkPath));
            apkSign.sign1("123456", "key0", "123456", false, Paths.get(sign_inApkPath), Paths.get(sign_outApkPath));

//            if (new File(outApkPath).exists()) {
//                new File(outApkPath).delete();
//            }
//            if (ziplan_inApkFile.exists()) {
//                ziplan_inApkFile.delete();
//            }
//            if (ziplan_outApkFile.exists()) {
//                ziplan_outApkFile.delete();
//            }
            FileUtil.deleteFileWithRetry(outApkPath);
            FileUtil.deleteFileWithRetry(ziplan_inApkPath);
            FileUtil.deleteFileWithRetry(ziplan_outApkPath);

        } catch (Exception e) {
            e.printStackTrace();
            log.error("合并失败: " + e.getMessage());
            throw new IOException("合并失败: " + e.getMessage());
        }finally {
            if (new File(outApkPath).exists()) {
                new File(outApkPath).delete();
            }
        }
    }

    /**
     * 删除Zip文件中多个指定后缀的所有文件
     *
     * @param zipPath   Zip文件路径
     */
    public void delFileWithApk(String zipPath){
        try (ZipFile zipFile = new ZipFile(zipPath)) {
            List<FileHeader> toDelete = new ArrayList<>();// 先收集所有需要删除的文件头
            List<FileHeader> fileHeaders = zipFile.getFileHeaders();
            for (FileHeader header : fileHeaders) {
                String entryName = header.getFileName();
                if (entryName != null) {
                    if (entryName.contains("/")){  // 跳过包含"/"的文件（保留子目录中的文件）
                        if (entryName.startsWith("META-INF/") || entryName.equals("META-INF")) {// 新增：检查是否是META-INF目录下的文件或META-INF目录本身
                            toDelete.add(header);
                        }
                        continue;
                    }
                    if (entryName.endsWith(".dex")) { // 删除.dex文件
                        toDelete.add(header);
                        continue;
                    }
                    if (entryName.equals("resources.arsc")) {// 删除resources.arsc
                        toDelete.add(header);
                        continue;
                    }
                    if (entryName.equals("AndroidManifest.xml")) {// 删除AndroidManifest.xml
                        toDelete.add(header);
                        continue;
                    }
                }
            }
            // 批量删除收集到的文件
            for (FileHeader header : toDelete) {
                zipFile.removeFile(header);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 将源APK文件中的内容复制到目标APK文件，保持路径一致，若目标中已存在则跳过
     *
     * @param sourceApkPath 源APK文件路径(A.apk)
     * @param targetApkPath 目标APK文件路径(B.apk)
     * @throws Exception 当IO操作出现错误时抛出
     */
    public void copyApkFiles(String sourceApkPath, String targetApkPath, Map<String, String> idMap){
        // 验证源文件是否存在
        File sourceFile = new File(sourceApkPath);
        if (!sourceFile.exists() || !sourceFile.isFile()) {
            throw new IllegalArgumentException("源APK文件不存在或不是有效文件: " + sourceApkPath);
        }
        // 初始化源和目标ZipFile对象，使用try-with-resources确保资源关闭
        try (ZipFile sourceZip = new ZipFile(sourceApkPath); ZipFile targetZip = new ZipFile(targetApkPath)) {
            // 获取源APK中的所有文件头
            List<FileHeader> fileHeaders = sourceZip.getFileHeaders();

            // 创建Zip参数，用于添加文件到目标APK
            ZipParameters zipParameters = new ZipParameters();
            zipParameters.setIncludeRootFolder(false); // 不包含根文件夹

            Map<String, FileHeader> xmlFileMap = new HashMap<>();

            // 遍历源APK中的每个文件
            for (FileHeader fileHeader : fileHeaders) {
                String fileName = fileHeader.getFileName();

                if (fileName.endsWith(".dex")) {
                    log.info("dex文件，跳过复制: " + fileName);
                    continue;
                }

                if (fileName.equals("resources.arsc")) {
                    log.info("resources.arsc文件，跳过复制: " + fileName);
                    continue;
                }

                if (fileName.equals("AndroidManifest.xml")) {
                    log.info("AndroidManifest.xml文件，跳过复制: " + fileName);
                    continue;
                }

                if (fileName.contains("META-INF")) {
                    log.info("META-INF文件，跳过复制: " + fileName);
                    continue;
                }

                // 检查目标APK中是否已存在该文件
                if (targetZip.getFileHeader(fileName) != null) {
                    log.info("文件已存在，跳过复制: " + fileName);
                    continue;
                }

                if (fileName.startsWith("res/") && fileName.endsWith(".xml")) {
                    xmlFileMap.put(fileName, fileHeader);
                    continue;
                }

                try (InputStream inputStream = sourceZip.getInputStream(fileHeader)) {
                    zipParameters.setFileNameInZip(fileName);
                    // 对于关键文件使用STORE模式（未压缩），其他文件使用DEFLATE压缩
                    if (fileName.equals("resources.arsc") || 
                        fileName.equals("AndroidManifest.xml") || 
                        fileName.startsWith("lib/") && fileName.endsWith(".so")) {
                        zipParameters.setCompressionMethod(CompressionMethod.STORE);
                    } else {
                        zipParameters.setCompressionMethod(CompressionMethod.DEFLATE);
                        zipParameters.setCompressionLevel(CompressionLevel.NORMAL);
                    }
                    targetZip.addStream(inputStream, zipParameters);
                    log.info("拷贝文件: " + fileName + " (压缩方式: " + zipParameters.getCompressionMethod() + ")");
                }
            }


            //向输出apk中添加已经修改好res/../*.xml
            for (Map.Entry<String, FileHeader> entry : xmlFileMap.entrySet()) {
                FileHeader xmlFileHeader = entry.getValue();
                String xmlFileName = entry.getKey();

                try (InputStream is = sourceZip.getInputStream(xmlFileHeader);){
                    byte[] xmlData = is.readAllBytes();
                    if (!AxmlUtil.isBinaryXml(xmlData)){
                        ZipParameters parameters = new ZipParameters();
                        parameters.setFileNameInZip(xmlFileName);
                        parameters.setCompressionMethod(xmlFileHeader.getCompressionMethod());
                        parameters.setCompressionLevel(CompressionLevel.NORMAL);
                        targetZip.addStream(new ByteArrayInputStream(xmlData), parameters);
                        continue; // 跳过后续二进制AXML处理逻辑
                    }

                    try {
                        AxmlReader reader = new AxmlReader(xmlData);
                        Axml axml = new Axml();
                        reader.accept(axml);

                        // 递归遍历所有节点并修改属性值
                        AxmlUtil.traverseAndModifyNodes(axml.firsts,idMap);

                        AxmlWriter writer = new AxmlWriter();
                        axml.accept(writer);
                        byte[] modifiedData = writer.toByteArray();

                        targetZip.removeFile(entry.getValue());

                        ZipParameters parameters = new ZipParameters();
                        parameters.setFileNameInZip(xmlFileName);
                        parameters.setCompressionMethod(CompressionMethod.DEFLATE);
                        parameters.setCompressionLevel(CompressionLevel.NORMAL);
                        targetZip.addStream(new ByteArrayInputStream(modifiedData), parameters);
                        log.info("修改id并拷贝文件: " + xmlFileName);
                    } catch (Exception e) {
                        log.error("修改id失败: " + xmlFileName);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

//        //honor
//        try {
//            AssetsConfig appConfig = new AssetsConfig();
//            appConfig.setChannel("oppo");
//            appConfig.setAppUrl("https://aurora.jxrcgame.com/");
//            appConfig.setAppDebug(false);
//            appConfig.setAppSoftnum("2025SA0002188");
//            appConfig.setAppSoftauthor("成都九霄融创科技有限公司");
//            appConfig.setAppCompanyCode(1);
//            appConfig.setAppName("卡车爬坡模拟器");
//            appConfig.setAppVersionName("1.0.2");
//            appConfig.setAppVersionCode(102);
//            appConfig.setAppPackage("com.kacppmnq.nearme.gamecenter");
//            appConfig.setAppAct("com.fakerandroid.boot.FakerActivity");
//            appConfig.setAppId("32824691");
//            appConfig.setAppKey("bfa2a47c5e8347be951b0efd6696a24f");
//            appConfig.setAppMediaid("ea5d5994e0a24014a934674f7bf4b3f5");
//            appConfig.setAppSecret("a1876dd4b96a4f929fee3216bdec3353");
//            appConfig.setAppScreenmode(false);
//            appConfig.setAppMode(0);
//            List<AssetsConfig.ConfigBean> logConfigs = new ArrayList<>();
//            logConfigs.add(new AssetsConfig.ConfigBean(1,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(3,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(4,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(7,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(8,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(10,-1,false,false,false,false,false));
//            appConfig.setConfig(logConfigs);
//            ApkMerge apkMerge = new ApkMerge(TaskLog.newTaskLog());
//            apkMerge.apkMerge(
//                    "C:/Users/Mayn/Desktop/frdss/honor/卡车爬坡模拟器.apk",
//                    "C:/Users/Mayn/Desktop/gdqqdtz/oppo/oppo-gdqqdtz-debug.apk",
//                    "C:/Users/Mayn/Desktop/frdss/honor/app_icon_kacppmnq.png",
//                    appConfig
//            );
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

//        try {
//            AssetsConfig appConfig = new AssetsConfig();
//            appConfig.setChannel("honor");
//            appConfig.setAppUrl("https://aurora.jxrcgame.com/");
//            appConfig.setAppDebug(false);
//            appConfig.setAppSoftnum("2024SA0084719");
//            appConfig.setAppSoftauthor("成都九霄融创科技有限公司");
//            appConfig.setAppCompanyCode(1);
//            appConfig.setAppName("我滚球超帅");
//            appConfig.setAppVersionName("1.0.2");
//            appConfig.setAppVersionCode(102);
//            appConfig.setAppPackage("com.wgqcs.honor");
//            appConfig.setAppAct("com.fakerandroid.boot.FakerActivity");
//            appConfig.setAppId("104524749");
//            appConfig.setAppKey("d88646854a7e4ff38f697cb175c03a80");
//            appConfig.setAppMediaid("1976178302692884480");
//            appConfig.setAppSecret("z532CziWD2bCMMBuFk+MyNeYY+g3VQDleCu3y4I5hfk=");
//            appConfig.setAppScreenmode(true);
//            appConfig.setAppMode(0);
//            List<AssetsConfig.ConfigBean> logConfigs = new ArrayList<>();
//            logConfigs.add(new AssetsConfig.ConfigBean(1,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(3,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(4,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(7,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(8,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(10,-1,false,false,false,false,false));
//            appConfig.setConfig(logConfigs);
//            ApkMerge apkMerge = new ApkMerge(TaskLog.newTaskLog());
//            apkMerge.apkMerge(
//                    "C:/Users/Mayn/Desktop/test/honor/滚动球球大挑战.apk",
//                    "C:/Users/Mayn/Desktop/test/honor/app-wdhhly-debug.apk",
//                    "C:/Users/Mayn/Desktop/test/honor/app_icon_wgqcs.png",
//                    appConfig
//            );
//        } catch (IOException e) {
//            e.printStackTrace();
//        }


//        //vivo
//        try {
//            AssetsConfig appConfig = new AssetsConfig();
//            appConfig.setChannel("vivo");
//            appConfig.setAppUrl("https://aurora.jxrcgame.com/");
//            appConfig.setAppDebug(false);
//            appConfig.setAppSoftnum("2024SA0036843");
//            appConfig.setAppSoftauthor("成都九霄融创科技有限公司");
//            appConfig.setAppCompanyCode(0);
//            appConfig.setAppName("滚动球球大挑战");
//            appConfig.setAppVersionName("1.0.8");
//            appConfig.setAppVersionCode(108);
//            appConfig.setAppPackage("com.gdqqdtz.vivo");
//            appConfig.setAppAct("com.fakerandroid.boot.FakerActivity");
//            appConfig.setAppId("105777990");
//            appConfig.setAppKey("745fa197bf56215c78e4f92b4d7f0772");
//            appConfig.setAppMediaid("ea5d5994e0a24014a934674f7bf4b3f5");
//            appConfig.setAppSecret("21c10496d42347129d01088f1e8fb99b");
//            appConfig.setAppScreenmode(true);
//            appConfig.setAppMode(0);
//            List<AssetsConfig.ConfigBean> logConfigs = new ArrayList<>();
//            logConfigs.add(new AssetsConfig.ConfigBean(1,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(3,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(4,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(7,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(8,-1,false,false,false,false,false));
//            logConfigs.add(new AssetsConfig.ConfigBean(10,-1,false,false,false,false,false));
//            appConfig.setConfig(logConfigs);
//            ApkMerge apkMerge = new ApkMerge(TaskLog.newTaskLog());
//            apkMerge.apkMerge(
//                    "C:/Users/Mayn/Desktop/test/vivo/滚动球球大挑战.apk",
//                    "C:/Users/Mayn/Desktop/test/vivo/vivo.apk",
//                    "C:/Users/Mayn/Desktop/test/vivo/app_icon_gdqqdtz_vivo.png",
//                    appConfig
//            );
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        //oppo
        try {
            AssetsConfig appConfig = new AssetsConfig();
            appConfig.setChannel("oppo");
            appConfig.setAppUrl("https://aurora.jxrcgame.com/");
            appConfig.setAppDebug(false);
            appConfig.setAppSoftnum("2024SA0036843");
            appConfig.setAppSoftauthor("成都九霄融创科技有限公司");
            appConfig.setAppCompanyCode(0);
            appConfig.setAppName("滚动球球大挑战");
            appConfig.setAppVersionName("1.1.9");
            appConfig.setAppVersionCode(119);
            appConfig.setAppPackage("com.jxrc.gdqqdtz.nearme.gamecenter");
            appConfig.setAppAct("com.fakerandroid.boot.FakerActivity");
            appConfig.setAppId("31736425");
            appConfig.setAppKey("fb30434146e04c28bd653d1c6910ff62");
            appConfig.setAppMediaid("87bfd34f0be5488d8054296b1cf88bd0");
            appConfig.setAppSecret("d34c72862dbc4d02ae2b9c04924ceca0");
            appConfig.setAppScreenmode(false);
            appConfig.setAppMode(0);
            List<AssetsConfig.ConfigBean> logConfigs = new ArrayList<>();
            logConfigs.add(new AssetsConfig.ConfigBean(1,-1,false,false,false,false,false));
            logConfigs.add(new AssetsConfig.ConfigBean(3,-1,false,false,false,false,false));
            logConfigs.add(new AssetsConfig.ConfigBean(4,-1,false,false,false,false,false));
            logConfigs.add(new AssetsConfig.ConfigBean(7,-1,false,false,false,false,false));
            logConfigs.add(new AssetsConfig.ConfigBean(8,-1,false,false,false,false,false));
            logConfigs.add(new AssetsConfig.ConfigBean(10,-1,false,false,false,false,false));
            appConfig.setConfig(logConfigs);
            ApkMerge apkMerge = new ApkMerge(TaskLog.newTaskLog());
            apkMerge.apkMerge(
                    "C:/Users/Mayn/Desktop/gdqqdtz/oppo/滚动球球大挑战.apk",
                    "C:/Users/Mayn/Desktop/gdqqdtz/oppo/oppo-gdqqdtz-debug.apk",
                    "C:/Users/Mayn/Desktop/gdqqdtz/oppo/app_icon.png",
                    appConfig
            );
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}